package controller

import (
	"archive/zip"
	"bytes"
	"encoding/base64"
	"fmt"
	"io"
	"lzppp/internal/app/response"
	"lzppp/internal/pkg/utils"
	"mime"
	"net/http"
	"net/url"
	"path"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
)

// UploadFileHandler 上传文件
// @Summary      上传文件
// @Description  上传文件
// @Tags         文件管理
// @Accept       multipart/form-data
// @Produce      json
// @Param        file  formData  file  true  "文件"
// @Param        folder  formData  string false  "文件夹，不传默认为 default"
// @Success      200  {object}  response.R
// @Router       /file/upload [post]
// UploadFileHandler 上传文件
func UploadFileHandler(c *gin.Context) {
	file, header, err := c.Request.FormFile("file")
	if err != nil {
		c.JSON(http.StatusBadRequest, response.Error(err.Error()))
		return
	}
	defer file.Close()

	// 上传目录
	folder := c.PostForm("folder")
	if folder == "" {
		folder = "default"
	}

	objectName := fmt.Sprintf("%s/%s", folder, header.Filename)

	size := header.Size
	contentType := header.Header.Get("Content-Type")

	if err := utils.MinioTool.UploadFile(objectName, file, size, contentType); err != nil {
		c.JSON(http.StatusInternalServerError, response.Error(err.Error()))
		return
	}

	c.JSON(http.StatusOK, response.Data(objectName))
}

// DownloadFileHandler godoc
// @Summary      下载或预览文件
// @Description  下载或预览文件
// @Tags         文件管理
// @Produce      application/octet-stream
// @Param        filename query string true "文件名"
// @Router       /file/download [get]
func DownloadFileHandler(c *gin.Context) {
	filename := c.Query("filename")
	obj, err := utils.MinioTool.DownloadFile(filename)
	if err != nil {
		c.JSON(http.StatusInternalServerError, response.Error(err.Error()))
		return
	}
	defer obj.Close()

	// 尝试自动识别文件类型
	ext := path.Ext(filename)
	mimeType := mime.TypeByExtension(ext)
	if mimeType == "" {
		mimeType = "application/octet-stream"
	}

	c.Header("Content-Type", mimeType)
	c.Header("Content-Disposition",
		fmt.Sprintf(`inline; filename="%s"; filename*=UTF-8''%s`, url.PathEscape(filename), url.PathEscape(filename)),
	)

	io.Copy(c.Writer, obj)
}

// DeleteFileHandler godoc
// @Summary      删除文件
// @Description  删除文件
// @Tags         文件管理
// @Produce      json
// @Param        filename query string true "文件名"
// @Success      200 {object} response.R
// @Router       /file/delete [delete]
func DeleteFileHandler(c *gin.Context) {
	filename := c.Query("filename")
	if err := utils.MinioTool.DeleteFile(filename); err != nil {
		c.JSON(http.StatusInternalServerError, response.Error(err.Error()))
		return
	}
	c.JSON(http.StatusOK, response.Data(filename))
}

// ListFilesHandler godoc
// @Summary      列出文件
// @Description  列出文件
// @Tags         文件管理
// @Produce      json
// @Param        prefix query string false "文件名"
// @Success      200  {object}  response.R
// @Router       /file/list [get]
func ListFilesHandler(c *gin.Context) {
	prefix := c.Query("prefix")
	files, err := utils.MinioTool.ListFiles(prefix)
	if err != nil {
		c.JSON(http.StatusInternalServerError, response.Error(err.Error()))
		return
	}
	c.JSON(http.StatusOK, response.Data(files))
}

// PresignedURLHandler godoc
// @Summary      获取预签名下载链接
// @Description  获取预签名下载链接
// @Tags         文件管理
// @Produce      json
// @Param        filename query string true "文件名"
// @Success      200 {object} response.R
// @Router       /file/presigned [get]
func PresignedURLHandler(c *gin.Context) {
	filename := c.Query("filename")
	expire := time.Minute * 10 // 默认10分钟
	uri, err := utils.MinioTool.PresignedURL(filename, expire)
	if err != nil {
		c.JSON(http.StatusInternalServerError, response.Error(err.Error()))
		return
	}
	c.JSON(http.StatusOK, response.Data(uri))
}

// DownloadZipHandler godoc
// @Summary      批量下载文件并打包成 zip
// @Description  通过逗号分隔的文件名，打包下载
// @Tags         文件管理
// @Produce      application/zip
// @Param        files query string true "文件名列表，用逗号分隔"
// @Router       /file/download-zip [get]
func DownloadZipHandler(c *gin.Context) {
	files := c.Query("files")
	if files == "" {
		c.JSON(http.StatusBadRequest, response.Error("缺少文件名"))
		return
	}
	fileList := strings.Split(files, ",")

	c.Header("Content-Type", "application/zip")
	c.Header("Content-Disposition", "attachment; filename=files.zip")

	zipWriter := zip.NewWriter(c.Writer)
	defer zipWriter.Close()

	for _, name := range fileList {
		obj, err := utils.MinioTool.DownloadFile(name)
		if err != nil {
			continue
		}

		w, err := zipWriter.Create(name)
		if err != nil {
			obj.Close()
			continue
		}

		_, copyErr := io.Copy(w, obj)
		obj.Close()
		if copyErr != nil {
			continue
		}
	}
}

// GetFolderSizeHandler godoc
// @Summary      获取文件夹大小
// @Description  获取指定文件夹下所有文件的大小，并支持单位换算
// @Tags         文件管理
// @Produce      json
// @Param        folder query string true "文件夹路径"
// @Param        unit   query string false "单位，可选：B, KB, MB, GB，默认 B"
// @Success      200  {object}  response.R
// @Router       /file/folder-size [get]
func GetFolderSizeHandler(c *gin.Context) {
	folder := c.Query("folder")
	if folder == "" {
		c.JSON(http.StatusBadRequest, response.Error("folder 参数不能为空"))
		return
	}

	unit := c.DefaultQuery("unit", "B") // 默认 B
	size, err := utils.MinioTool.GetFolderSize(folder)
	if err != nil {
		c.JSON(http.StatusInternalServerError, response.Error(err.Error()))
		return
	}

	convertedSize := float64(size)
	switch unit {
	case "KB":
		convertedSize = convertedSize / 1024
	case "MB":
		convertedSize = convertedSize / (1024 * 1024)
	case "GB":
		convertedSize = convertedSize / (1024 * 1024 * 1024)
	case "B":
		// 原样返回
	default:
		c.JSON(http.StatusBadRequest, response.Error("unit 参数错误，只能是 B, KB, MB, GB"))
		return
	}

	c.JSON(http.StatusOK, response.Data(map[string]interface{}{
		"folder": folder,
		"size":   convertedSize,
		"unit":   unit,
	}))
}

// UploadBase64Handler godoc
// @Summary      上传 Base64 图片
// @Description  接收 Base64 编码的图片并上传到 MinIO
// @Tags         文件管理
// @Accept       json
// @Produce      json
// @Param        request  body  UploadBase64Request  true  "请求体"
// @Success      200  {object}  response.R
// @Router       /file/upload-base64 [post]
func UploadBase64Handler(c *gin.Context) {
	var req struct {
		FileBase64 string `json:"fileBase64" binding:"required"`
		FileName   string `json:"fileName" binding:"required"`
		Folder     string `json:"folder"` // 可选，默认 default
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, response.Error(err.Error()))
		return
	}

	// 去掉前缀，例如 data:image/png;base64,
	data := req.FileBase64
	if strings.Contains(data, ",") {
		data = strings.Split(data, ",")[1]
	}

	// 解码 Base64
	fileBytes, err := base64.StdEncoding.DecodeString(data)
	if err != nil {
		c.JSON(http.StatusBadRequest, response.Error("Base64 解码失败: "+err.Error()))
		return
	}

	// 上传目录
	folder := req.Folder
	if folder == "" {
		folder = "default"
	}

	objectName := fmt.Sprintf("%s/%s", folder, req.FileName)

	// 上传
	if err := utils.MinioTool.UploadFile(objectName, bytes.NewReader(fileBytes), int64(len(fileBytes)), "image/png"); err != nil {
		c.JSON(http.StatusInternalServerError, response.Error("上传失败: "+err.Error()))
		return
	}

	c.JSON(http.StatusOK, response.Data(objectName))
}

// UploadBase64Request 用于 swagger 文档的请求体定义
type UploadBase64Request struct {
	FileBase64 string `json:"fileBase64" example:"..."` // Base64 内容
	FileName   string `json:"fileName" example:"test.png"`                                            // 文件名
	Folder     string `json:"folder" example:"images"`                                                // 文件夹，可选
}
